package me.android.library.utils.android.graphic;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;

import com.google.common.io.BaseEncoding;

import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import me.android.library.utils.android.ContextUtil;
import mylab.utils.common.FileUtil;
import mylab.utils.common.IoUtil;


/**
 * 尽量不要用 BitmapFactory.decodeResource 当使用像
 * <p/>
 * 当使用像imageView.setBackgroundResource，imageView.setImageResource, 或者
 * BitmapFactory.decodeResource 这样的方法来设置一张大图片的时候，
 * 这些函数在完成decode后，最终都是通过java层的createBitmap来完成的，需要消耗更多内存。
 * 因此，改用先通过BitmapFactory.decodeStream方法，创建出一个bitmap，再将其设为ImageView的
 * source，decodeStream最大的秘密在于其直接调用JNI
 * >>nativeDecodeAsset()来完成decode，无需再使用java层的createBitmap
 * ，从而节省了java层的空间。如果在读取时加上图片的Config参数，可以跟有效减少加载的内存，从而跟有效阻止抛out of Memory异常。
 *
 * @author sylar
 */

@Slf4j
@UtilityClass
public class BitmapUtil {

    BitmapFactory.Options defaultOptions;

    static {
        defaultOptions = new BitmapFactory.Options();
        defaultOptions.inPreferredConfig = Bitmap.Config.RGB_565;
    }

    /**
     * Bitmap缩放
     */
    public Bitmap zoomBySize(Bitmap bitmap, int width, int height) {
        int w = bitmap.getWidth();
        int h = bitmap.getHeight();
        Matrix matrix = new Matrix();
        float scaleWidth = ((float) width / w);
        float scaleHeight = ((float) height / h);
        matrix.postScale(scaleWidth, scaleHeight);
        return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
    }

    /**
     * Bitmap转换成Drawable
     */
    public Drawable toDrawable( Bitmap bmp) {
        return new BitmapDrawable(ContextUtil.getResources(), bmp);
    }

    /**
     * 将Drawable转化为Bitmap
     */
    public Bitmap fromDrawable(Drawable drawable) {
        // 取 drawable 的长宽
        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();

        // 取 drawable 的颜色格式
        Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
                : Bitmap.Config.RGB_565;
        // 建立对应 bitmap
        Bitmap bitmap = Bitmap.createBitmap(w, h, config);
        // 建立对应 bitmap 的画布
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, w, h);
        // 把 drawable 内容画到画布中
        drawable.draw(canvas);
        return bitmap;
    }

    public String toBase64(Bitmap bmp) throws IOException {
        return toBase64(bmp, Bitmap.CompressFormat.JPEG, 80);
    }

    public String toBase64(Bitmap bmp, int quality) throws IOException {
        return toBase64(bmp, Bitmap.CompressFormat.JPEG, quality);
    }

    /**
     * 图片转字符串表示
     */
    public String toBase64(Bitmap bmp, Bitmap.CompressFormat format, int quality) throws IOException {

        String res = null;
        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            bmp.compress(format, quality, stream);
            res = BaseEncoding.base64().encode(stream.toByteArray());
        }
        return res;
    }

    public Bitmap fromBase64(String base64String) {
        byte[] bytes = BaseEncoding.base64().decode(base64String);
        return fromBytes(bytes);
    }

    /**
     * Bitmap 转 byte[]
     */
    public byte[] toBytes(Bitmap bmp) throws IOException {
        return toBytes(bmp, Bitmap.CompressFormat.PNG, 100);
    }


    /**
     * Bitmap 转 byte[]
     */
    public byte[] toBytes(Bitmap bmp, Bitmap.CompressFormat format, int quality) throws IOException {
        if (quality < 0 || quality > 100) {
            quality = 100;
        }
        InputStream in = bitmap2stream(bmp, format, quality);
        return IoUtil.read(in).toByteArray();
    }

    /**
     * byte[] 转 Bitmap
     */
    public Bitmap fromBytes(byte[] b) {
        return (b == null || b.length == 0) ? null :
                BitmapFactory.decodeByteArray(b, 0, b.length);
    }

    /**
     * 将Bitmap转换成InputStream
     */
    public InputStream bitmap2stream(Bitmap bmp, Bitmap.CompressFormat format, int quality) throws IOException {
        InputStream is;

        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            bmp.compress(format, quality, stream);
            is = new ByteArrayInputStream(stream.toByteArray());
        }
        return is;
    }

    // -----------------------------------------------------------------------------
    //
    // -----------------------------------------------------------------------------

    /**
     * uri to bitmap
     */
    public Bitmap fromUri( Uri uri) {
        Bitmap bitmap = null;
        try (InputStream stream = ContextUtil.getContentResolver().openInputStream(uri);) {
            bitmap = BitmapFactory.decodeStream(stream);
        } catch (IOException e) {
            log.warn("fromUri error", e);
        }
        return bitmap;
    }


    public Bitmap fromResource(int resid) {
        return fromResource(resid, defaultOptions);
    }

    public Bitmap fromResource( int resid, BitmapFactory.Options option) {
        InputStream is = ContextUtil.getResources().openRawResource(resid);
        return BitmapFactory.decodeStream(is, null, option);
    }

    public Bitmap fromFile(String fileName) {
        return BitmapFactory.decodeFile(fileName, defaultOptions);
    }

    public Bitmap fromFile(String fileName, BitmapFactory.Options option) {
        return BitmapFactory.decodeFile(fileName, option);
    }

    public File toFile(Bitmap bmp, String fullFileName) throws IOException {
        File file = FileUtil.newFile(fullFileName);

        try (BufferedOutputStream stream = new BufferedOutputStream(new FileOutputStream(file))) {
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, stream);
            stream.flush();
        }

        return file;
    }

    /**
     * 按指定尺寸转换资源图片
     *
     * @param reqWidth  请求的宽度（控件宽度）
     * @param reqHeight 请求的高度（控件高度）
     */
    public Bitmap decodeSampledBitmapFromResource( int resId, int reqWidth, int reqHeight) {
        Resources res = ContextUtil.getResources();

        // 第一次解析将inJustDecodeBounds设置为true，来获取图片大小
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        // 调用上面定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    /**
     * 计算最匹配的采样数
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        options.inJustDecodeBounds = true;
        // 源图片的高度和宽度
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            // 计算出实际宽高和目标宽高的比率
            final int heightRatio = Math.round((float) height
                    / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = Math.min(heightRatio, widthRatio);
        }
        return inSampleSize;
    }
}
